import asyncio
import logging
import threading
from django.apps import AppConfig
from django.conf import settings

logger = logging.getLogger(__name__)

def start_background_loop(loop: asyncio.AbstractEventLoop) -> None:
    """
    Starts a new event loop in a background thread.
    """
    asyncio.set_event_loop(loop)
    loop.run_forever()

async def check_device_liveness():
    """
    A background task that periodically checks for devices that have gone offline.
    """
    from django.utils import timezone
    from datetime import timedelta
    from channels.layers import get_channel_layer
    from .models import Device
    from channels.db import database_sync_to_async

    @database_sync_to_async
    def get_offline_devices(timeout_seconds):
        timeout_threshold = timezone.now() - timedelta(seconds=timeout_seconds)
        offline_devices = list(Device.objects.filter(
            status='online',
            last_seen__lt=timeout_threshold
        ))
        if offline_devices:
            Device.objects.filter(pk__in=[d.pk for d in offline_devices]).update(status='offline')
        return offline_devices

    @database_sync_to_async
    def device_to_dict(device):
        return {
            'id': device.pk, 'device_id': device.device_id, 'name': device.name,
            'location': device.location, 'status': 'offline', 'ip_address': device.ip_address,
            'firmware_version': device.firmware_version,
            'last_seen': device.last_seen.isoformat() if device.last_seen else None,
            'created_at': device.created_at.isoformat(),
        }

    channel_layer = get_channel_layer()
    
    while True:
        await asyncio.sleep(30)  # Check every 30 seconds
        try:
            # Timeout threshold is 30 seconds
            offline_devices = await get_offline_devices(timeout_seconds=30)
            
            for device in offline_devices:
                logger.info(f"Device {device.device_id} timed out. Marking as offline.")
                await channel_layer.group_send(
                    'admin_monitor',
                    {
                        'type': 'device_status_update',
                        'payload': await device_to_dict(device)
                    }
                )
        except Exception as e:
            logger.error(f"Error in device liveness check task: {e}", exc_info=True)


class AccessControlConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'access_control'
    task_started = False

    def ready(self):
        # This check prevents the code from running twice in development mode with the reloader.
        # It also ensures the task is started only once.
        if not self.task_started and settings.SETTINGS_MODULE == 'config.settings':
            self.task_started = True
            logger.info("Starting device liveness check background thread...")
            
            # Create a new event loop for the background thread
            loop = asyncio.new_event_loop()
            
            # Start the event loop in a new daemon thread
            t = threading.Thread(target=start_background_loop, args=(loop,), daemon=True)
            t.start()
            
            # Schedule the async task in the new loop
            asyncio.run_coroutine_threadsafe(check_device_liveness(), loop)
